-- MSG
-- Create by weism
-- 消息ID的维护管理

module("MSG", package.seeall);

-- 消息名字与ID的映射关系
local cmdIdMap = {};
local idNameMap = {};

-- 消息ID与处理器的映射关系
local cmdModules = {};

-- 所有的消息指令
local msgQ = {};

-- 等待消息回调
local waitMsgList = {};
local msgTrigger  = {};

-- 是否打印消息
local debugPrintMsg = false;

-- 验证线程消息回调
local verifyMsgHandler = nil;

-- 内部函数声明
local loadSendFile;
local loadCmdModules;

-- 模块初始化
function init()
    loadSendFile();
    loadCmdModules();

    -- 掉线的处理
    EventMgr.removeAll("Communicate");
    EventMgr.register("Communicate", event.NETWORK_DISCONNECTED, function()
        -- 所有的消息处理器丢弃
        msgQ = {};
    end);
end

-- 根据消息名字取得消息ID
function getMsgId(msg)
    return cmdIdMap[string.upper(msg)];
end

-- 根据消息ID获取消息名
function getMsgName(id)
    return idNameMap[id];
end

-- 添加一条消息
function addMsg(cmdNo, para)
    --cclog("addMsg cmdNo : %o, para : %o\n", cmdNo, para);
    -- 如果是心跳信息，直接处理掉
    if cmdNo == 0xff01 then
        Communicate.sendRaw(0xff02, para.body);
        return;
    end
    table.insert(msgQ, { cmdNo = cmdNo, para = para });
end

-- 把消息添加到待处理队列最前面
-- 用于MSG_ZLIB_MSG
function addMsgFirst(cmdNo, para)
    table.insert(msgQ, 1, { cmdNo = cmdNo, para = para });
end

-- 注册消息回调
function registerMsgTrigger(key, cmd, f)
    msgTrigger[cmd] = msgTrigger[cmd] or {};

    msgTrigger[cmd][key] = f;
end

-- 注册验证消息回调函数
function registerVerifyMsgHandler(f)
    verifyMsgHandler = f;
end

-- 调试开关的开启与关闭
function debugOn()  debugPrintMsg = true  end
function debugOff() debugPrintMsg = false end

-- 上次处理时间
local lastMsgHandleTime = {};

-- 不停的处理消息
function update()
    while msgQ[1] ~= nil do
        local cmdNo = msgQ[1].cmdNo;
        local para = msgQ[1].para;
        local cmdName = idNameMap[cmdNo];
        if DEBUG_MODE == 1 and cmdName == DELAY_MSG_NAME then
            if lastMsgHandleTime[cmdName] == nil then
                lastMsgHandleTime[cmdName] = os.time();
            end

            if os.time() - lastMsgHandleTime[cmdName] < DELAY_MSG_TIME then
                return;
            end

            print(string.format("消息[%s]延迟%d秒到达", cmdName, DELAY_MSG_TIME));
            lastMsgHandleTime[cmdName] = nil;
        end

        if debugPrintMsg then
            trace("MSG", "====== %s ======\n%s\n", idNameMap[cmdNo], LPC.saveString(para));
        end

        -- 先将消息摘除掉(防止消息处理中报错，就进入死循环了)
        table.remove(msgQ, 1);

        -- 开始处理消息
        local f = cmdModules[cmdNo];
        if f == "TBL" then
            local r = LOAD_RUN("game/cmd/" .. string.lower(idNameMap[cmdNo]));
            cmdModules[cmdNo] = r;
            f = r;
        end

        if not f then
            trace("MSG", "消息处理器(%d)不存在", cmdNo);
        else
            Profiler.funcBegin("MSG:" .. idNameMap[cmdNo]);
            f(para);
            Profiler.funcEnd("MSG:" .. idNameMap[cmdNo]);
        end

        -- 消息到达
        messageArrival(idNameMap[cmdNo], para)
    end

    -- 清除过期的等待
    local curTime = os.time()
    for _, cmd in pairs(table.keys(waitMsgList)) do
        local info = waitMsgList[cmd]
        if info.wait_time ~= -1 and info.wait_time < curTime then
            waitMsgList[cmd] = nil
        end
    end

    -- 处理验证线程的消息
--    local msg = cc.VerifyAgent:getInstance():recvMsg();
--    if msg ~= "" and verifyMsgHandler then
--        verifyMsgHandler(msg);
--    end
end

-- 消息到达
function messageArrival(cmd, para)
    -- 这条消息处理完毕了
    EventMgr.fire(event.MESSAGE_DONE, { cmd, para });

    local cbList = msgTrigger[cmd] or {};
    for _, f in pairs(cbList) do
        if type(f) == "function" then
            f(para);
        end
    end

    local m = waitMsgList[cmd]
    if not m then
        return
    end

    if  m.wait_time == -1 or
        m.wait_time >= os.time() then
        m.call_back(para)
    end

    waitMsgList[cmd] = nil
end

-- 注册等待消息
function waitMessageArrival(cmdNo, f, waitTime)
    waitTime = waitTime or -1

    waitMsgList[cmdNo] = {["call_back"] = f, ["wait_time"] = waitTime + os.time(), }
end

-- 载入通讯文件
loadSendFile = function()
    local content = cc.FileUtils:getInstance():getStringFromFile("comm/send.txt");
    if string.sub(content, 1, 3) == "\x11\x12\x13" then
        content = cc.FileUtils:getInstance():getStringXorResult(string.sub(content, 4), 123);
    end

    local lines = string.explode(content, '\n')

    for _,line in pairs(lines) do
        line = string.trim(line);
        -- 过滤掉空行
        if line ~= "" then
            -- #define ALL_MESSAGES 0x0800
            local _, __, name, id = string.find(line, "#define[ ]+([%w_0-9]+)[ ]+0[xX]+([0-9a-fA-F]+)");
            if id then
                id = tonumber(id, 16);
                cmdIdMap[string.upper(name)] = id;
                idNameMap[id] = string.upper(name);
            end
        end
    end
end

-- 载入所有的消息处理器
loadCmdModules = function()
    local r = LOAD_PATH2("game/cmd");
    for k, v in pairs(r) do
        local id = getMsgId(k);
        if not id then printTable(cmdIdMap, "cmdIdMap") error("没有定义消息：" .. k) end

        cmdModules[id] = v;
    end
end

_G["global_dispatch_command"] = MSG.addMsg;
